package ru.zombator.taskman.core.query;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Обеспечивает передачу запросов серверу и прием ответов от него.
 * 
 * @author nvamelichev
 * 
 */
public class Client implements IClient {
	/**
	 * таймаут ожидания ответа от сервера - 30 секунд
	 */
	private final int WAIT_TIMEOUT = 30;
	/**
	 * сервис посылки пакетов-запросов на сервер для обработки
	 */
	private ExecutorService svcSendAndWait = Executors.newCachedThreadPool();

	/**
	 * Клиентский объект, получающий пакеты-запросы и передающий ответы на них
	 * от сервера
	 */
	private IResponderCli client;

	/**
	 * Задача отправки пакета-запроса и получения ответа
	 * 
	 */
	private class SendPacketTask implements Callable<ITypedPacket> {
		private ITypedPacket whatToSend;
		private IResponderCli client;

		public SendPacketTask(IResponderCli client, ITypedPacket whatToSend) {
			this.whatToSend = whatToSend;
			this.client = client;
		}

		@Override
		public ITypedPacket call() throws Exception {
			IPacket response = client.getResponse(whatToSend.asUntyped());
			return new TypedPacket(response);
		}
	}

	@Override
	public ITypedPacket sendPacket(final ITypedPacket packet)
			throws TimeoutException {
		try {
			List<Future<ITypedPacket>> results =
				svcSendAndWait.invokeAll(Collections.singleton(new SendPacketTask(
					client, packet)), WAIT_TIMEOUT, TimeUnit.SECONDS);
			return results.get(0).get();
		} catch (CancellationException e) {
			throw new TimeoutException();
		} catch (InterruptedException e) {
			e.printStackTrace();
			return null;			
		} catch (ExecutionException e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public void dispose() {
		svcSendAndWait.shutdown();
	}

	public Client(IResponderCli client) {
		this.client = client;
	}

}
